package mymyoux.io
{
	import mymyoux.events.ErrorEvent;
	import mymyoux.events.MAdvSocketEvent;
	import mymyoux.events.MEventDispatcher;
	import mymyoux.events.MServerConnectorEvent;
	import mymyoux.events.MServerIdentifierEvent;
	import mymyoux.io.mserverconnectorClasses.MAdvSocket;
	import mymyoux.io.mserverconnectorClasses.MPlaylist;
	import mymyoux.io.mserverconnectorClasses.MRoom;
	import mymyoux.io.mserverconnectorClasses.MServerIdentifier;
	import mymyoux.utils.Hash;
	import mymyoux.utils.StringUtils;
	     /**
         * MServerconnector dispatche l'event connect
         * quand un MAdvSocket s'est connecté.
         * @eventType mymyoux.events.MServerConnectorEvent
         */
        [Event(name="connect", type="mymyoux.events.MServerConnectorEvent")]
          /**
         * MServerconnector dispatche l'event close
         * quand un MAdvSocket s'est déconnecté.
         * @eventType mymyoux.events.MServerConnectorEvent
         */
        [Event(name="close", type="mymyoux.events.MServerConnectorEvent")]
          /**
         * MServerconnector dispatche l'event error
         * quand un MAdvSocket a dispatché une erreur.
         * @eventType mymyoux.events.MServerConnectorEvent
         */
        [Event(name="error", type="mymyoux.events.MServerConnectorEvent")]
	/**
	 * MServerConnector permet de se connecter facilement à un réseau de MServeurs
	 * @author Mymyoux
	 * @version 2.0
	 */
	public class MServerConnector extends MEventDispatcher
	{
		/**
		 * Erreur renvoyée si l'ip spécifiée est vide
		 * ou si le port spéciée est en dehors de l'intervalle ]1024;65355[
		 * ou si le nom du serveur spécifié est vide ou a la valeur "default"
		 */
		static public const INCORRECT_DATA:uint=2;
		/**
		 * Erreur renvoyée si le nom du serveur spécifié est déjà utilisé
		 */
		static public const NAME_ALREADY_EXIST:uint=1;
		/**
		 * Erreur renvoyée si vous essayez de créer un salon alors qu'aucun serveur n'a été créée
		 */
		static public const NO_SERVER:uint=0;
		/**
		 * Associe à chaque  serveur(MadvSocket) un ou plusieurs noms
		 */
		protected var hServer:Hash;
		/**
		 * @private
		 */
		protected var identifiant:MServerIdentifier;
		/**
		 * @private
		 */
		 protected var playlist:MPlaylist;
		 /**
		 * Liste les salons ouverts.
		 */
		 protected var rooms:Hash;
		/**
		 * Crée un nouvel objet MServerConnector
		 */
		public function MServerConnector(identifiant:MServerIdentifier)
		{
			this.identifiant=identifiant;
			this.identifiant.addEventListener(MServerIdentifierEvent.UPDATE,updateProfil);
			hServer=new Hash();
			playlist=new MPlaylist(this.identifiant);
			rooms=new Hash();
		}
		/**
		 * MServerIdentifier représentant le joueur. Ses modifications sont envoyés aux MServeurs.
		 */
		 public function get contacts():MPlaylist{
		 	return playlist;
		 }
		/**
		 * Met à jour le profil sur tous les MServeurs.
		 * Appelé lors d'une modification de identifiant par la fonction put ou rem.
		 * L'appel est ignoré si l'appel à put ou rem n'est pas nécessaire.
		 */
		 protected function updateProfil(e:MServerIdentifierEvent):void{
		 	try{
		 		var a:Array=hServer.getValues(true);
		 		for(var i:String in a){
		 			try{
		 				(a[i] as MAdvSocket).send("x"+identifiant.serialize());
		 			}catch(e:Error){
		 			
		 			}
		 		}
		 	}catch(e:Error){
		 		
		 	}
		 }
		 /**
		 * MServerIdentifier représentant le joueur.
		 * S'il est modifié celà se répercute immédiatement sur le MServeur.
		 */
		 public function get me():MServerIdentifier{
		 		return identifiant;		 	
		 }
		 /**
		 * @inheritDoc 
		 */
		 public override function destroy():void{
		 	try{
		 		var a:Array=hServer.getValues(true);
		 		for(var i:String in a){
		 			(a[i] as MAdvSocket).destroy();
		 		}
		 	}catch(e:Error){
		 		
		 	}
		 	hServer=new Hash();
		 	playlist.destroy();
		 	super.destroy();
		 }
		/**
		 * Ajoute un serveur à la liste des connexions
		 * @param name	Nom du serveur
		 * @param ip	Ip du serveur
		 * @param port	Port du serveur
		 * @param password	Mot de passe nécessaire à l'acceptation par le serveur(vide par défaut)
		 * @throws mymyoux.events.ErrorEvent Renvoie une erreur si le nom ou l'ip sont vides ou si le port est incorrect
		 * @return void
		 */
		public function addServer(name:String,ip:String,port:uint,password:String=""):void{
			if(name.length==0 || ip.length==0 ||port<1024 || port>65355 || name=="default"){
				throw(new ErrorEvent("Nom, port ou ip incorrect",INCORRECT_DATA));
			}
			if(hServer.containsKey(name)){
				throw(new ErrorEvent("nom de serveur déjà existant",NAME_ALREADY_EXIST));
			}
			//Si le serveur existe déjà sous un autre nom on duplique son lien sinon on en crée un nouveau
			var m:MAdvSocket=new MAdvSocket(ip,port,password);
			var a:Array=hServer.getKeys(m);
			if(a.length==0){
				//si c'est le premier serveur d'enregistré
				if(hServer.length==0){
					hServer.put("default",m);
				}
				m.addEventListener(MAdvSocketEvent.CONNECT,onConnect);
				m.addEventListener(MAdvSocketEvent.CLOSE,onClose);
				m.addEventListener(MAdvSocketEvent.ERROR,onError);
				m.addEventListener(MAdvSocketEvent.ROOM_CLOSED,onRoomClosed);
				playlist.addServer(m);
				m.start();
				m.send("x"+identifiant.serialize());
			}else{
				m=hServer.get(a[0]) as MAdvSocket;
			}
			hServer.put(name,m);
		}
		/**
		 * Supprime un serveur à la liste des connexions. La fonction n'effectue aucune action si le serveur n'existe pas.
		 * @param name	Nom du serveur à supprimer.
		 */
		public function remServer(name:String):void{
			if(hServer.containsKey(name)){
				try{
					var m:MAdvSocket=hServer.get(name) as MAdvSocket;
					hServer.remove(name);
					//supprime le socket ainsi que les évènements associés s'il n'existe pas sous un autre nom
					if(!hServer.containsValue(m)){
						playlist.remServer(m);
						m.destroy();
						if(hServer.length==1){
							hServer.remove("default");
						}
					}
				}catch(e:Error){
				}
				
			}		
		}
		/**
		 * Essaye de reconnecter un MAdvSocket qui s'est déconnecté ou a rencontré une erreur. Ne fais rien si le nom est incorrect ou le Socket toujours connecté
		 * @param name	Nom du serveur
		 * @return void
		 */
		public function tryReconnectServer(name:String):void{
			if(hServer.containsKey(name)){
				try{
					var m:MAdvSocket=hServer.get(name) as MAdvSocket;
					if(!m.connected){
						m.restart();
					}
				}catch(e:Error){
				}
			
			}
		}
		/**
		 * Créée un nouveau salon
		 * @param name	Nom du salon
		 * @throws	mymyoux.events.ErrorEvent Renvoie une erreur si aucun serveur n'a été ajouté préalablement
		 * @return MRoom	le salon correspondant
		 */
		public function addRoom(name:String):MRoom{
			if(name.length>0){
				if(hServer.length==0){
					throw(new ErrorEvent("Il faut préalablement indiqué un serveur",NO_SERVER));
				}
				var roomName:String=name;
				var s:MAdvSocket;
				var prefixe:String;
				var tab:Array;
				if(StringUtils.count(roomName,"#")>=2){
					name=roomName.substring(roomName.indexOf("#")+1,roomName.indexOf("#",roomName.indexOf("#")+1));
					if(hServer.containsKey(name)){
						s=hServer.get(name) as MAdvSocket;
					}else{
						s=hServer.get("default") as MAdvSocket;
						tab=hServer.getKeys(s);
						if(tab.length>1){
							prefixe=(tab[0]=="default")?tab[1]:tab[0];
						}else{
							prefixe="default";
						}
						roomName=roomName.substring(roomName.indexOf("#",roomName.indexOf("#")+1)+1);
						roomName="#"+prefixe+"#"+roomName;
					}
				}else{
					s=hServer.get("default") as MAdvSocket;
					tab=hServer.getKeys(s);
					if(tab.length>1){
						prefixe=(tab[0]=="default")?tab[1]:tab[0];
					}else{
						prefixe="default";
					}
					roomName="#"+prefixe+"#"+roomName;
				}
				if(!rooms.containsKey(name)){
					var r:MRoom=new MRoom(roomName,s,playlist);
					rooms.put(roomName,r);
					return r;
				}else{
					return rooms.get(name) as MRoom;
				}			
			}
			return null;
		}
		/**
		 * Appelée lorsqu'un salon se ferme.
		 * Permet de retirer celui-ci de la liste des salons.
		 * @param e MAdvSocketEvent envoyé par le MAdvSocket qui hébergeait le salon.
		 */
		protected function onRoomClosed(e:MAdvSocketEvent):void{
			if(e.data!=null){
				if(e.data.length>0){
					if(rooms.containsKey(e.data)){
						rooms.remove(e.data);
					}		
				}
			}
		}
		/**
		 * Renvoie un boolean indiquant si oui ou non le salon a déjà été créée.
		 * @param name Nom du salon.
		 * @return true si le salon a déjà été créée, false sinon.
		 */
		public function hasRoom(name:String):Boolean{
			var roomName:String=name;
			var s:MAdvSocket;
			var prefixe:String;
			var tab:Array;
			if(StringUtils.count(roomName,"#")>=2){
				name=roomName.substring(roomName.indexOf("#")+1,roomName.indexOf("#",roomName.indexOf("#")+1));
				if(!hServer.containsKey(name)){
					s=hServer.get("default") as MAdvSocket;
					tab=hServer.getKeys(s);
					if(tab.length>1){
						prefixe=(tab[0]=="default")?tab[1]:tab[0];
					}else{
						prefixe="default";
					}
					roomName=roomName.substring(roomName.indexOf("#",roomName.indexOf("#")+1)+1);
					roomName="#"+prefixe+"#"+roomName;
				}
			}else{
				s=hServer.get("default") as MAdvSocket;
				tab=hServer.getKeys(s);
				if(tab.length>1){
					prefixe=(tab[0]=="default")?tab[1]:tab[0];
				}else{
					prefixe="default";
				}
				roomName="#"+prefixe+"#"+roomName;
			}
			return rooms.containsKey(roomName);
		}
		/**
		 * Appelé à la connexion d'un serveur (MadvSocket)
		 * @param e	MadvSocketEvent envoyé lors de la connexion
		 * @return void
		 */
		protected function onConnect(e:MAdvSocketEvent):void{
			genericDispatchEvent(e.target,MServerConnectorEvent.CONNECT);
			
		}
		/**
		 * Appelé à la fermeture d'un serveur (MadvSocket)
		 * @param e	MadvSocketEvent envoyé lors de la fermeture
		 * @return void
		 */
		protected function onClose(e:MAdvSocketEvent):void{
			//TODO:MServerConnectorEvent
			(e.target as MAdvSocket).send("x"+identifiant.serialize());
			genericDispatchEvent(e.target,MServerConnectorEvent.CLOSE);
		}
		/**
		 * Appelé lors d'une erreur d'un serveur (MadvSocket)
		 * @param e	MadvSocketEvent envoyé lors d'une erreur
		 * @return void
		 */
		protected function onError(e:MAdvSocketEvent):void{
			//TODO:MServerConnectorEvent
			genericDispatchEvent(e.target,MServerConnectorEvent.ERROR);
		}
		/**
		 * Dispatche les evenements de connexion,fermeture et erreur en les associants aux noms de serveurs correspondants
		 * @param o	Element qui dispatch l'evenement à la base
		 * @param s	ype de l'évènement dispatché
		 * @return void
		 */
		protected function genericDispatchEvent(o:Object,s:String):void{
			var a:Array=hServer.getKeys(o);
			//trace(o.equals(hServer.get("vache")));
			for(var i:String in a){
				if(a[i]!="default"){//default est interne au fonctionnement de MServerConnector
					dispatchEvent(new MServerConnectorEvent(s,a[i]));
				}
			}
		}

	}
}